<?php namespace App\Application;

use App\Exceptions\User\PublisherException;
use App\Exceptions\User\UserException;
use App\Repositories\Eloquent\User\Entity\SocialiteUser;
use App\Repositories\User\PublisherRepositoryInterface;
use App\Repositories\User\SocialiteUserRepositoryInterface;
use App\Repositories\User\UserLoginCodeRepositoryInterface;
use App\Repositories\User\UserRepositoryInterface;
use App\Repositories\User\UserTokenRepositoryInterface;
use Carbon\Carbon;


class UserService
{
    /**
     * @var UserRepositoryInterface
     */
    protected $user_repository;

    /**
     * @var SocialiteUserRepositoryInterface
     */
    protected $socialite_user_repository;

    /**
     * @var PublisherRepositoryInterface
     */
    protected $publisher_repository;

    /**
     * @var UserLoginCodeRepositoryInterface
     */
    protected $user_login_code_repository;

    /**
     * @var UserTokenRepositoryInterface
     */
    protected $user_token_repository;

    public function __construct()
    {
        $this->user_repository = app(UserRepositoryInterface::class);
        $this->socialite_user_repository = app(SocialiteUserRepositoryInterface::class);
        $this->publisher_repository = app(PublisherRepositoryInterface::class);
        $this->user_login_code_repository = app(UserLoginCodeRepositoryInterface::class);
        $this->user_token_repository = app(UserTokenRepositoryInterface::class);
    }

    /**
     * 获取用户信息
     *
     * @param $user_id
     *
     * @return \Illuminate\Database\Eloquent\Model|null
     * @throws UserException
     */
    public function show($user_id)
    {
        $user = $this->user_repository->show($user_id);
        if (!$user) {
            throw new UserException(UserException::NOT_FOUND);
        }

        return $user;
    }

    /**
     * 获取用户在微信公众平台的open id
     *
     * @param $user_id
     *
     * @return string|null
     */
    public function getWechatMpOpenId($user_id)
    {
        return $this->socialite_user_repository->getWechatMpOpenId($user_id);
    }

    /**
     * 获取用户登陆Code
     *
     * @param SocialiteUser $socialite_user
     *
     * @return string
     */
    public function getLoginCode(SocialiteUser $socialite_user)
    {
        $user_id = $this->getUserIdBySocialiteUser($socialite_user);

        $code = random_token('sha256');
        $expired_at = Carbon::now()->addMinutes((int)config('auth.login.code_expire'));

        $ip = $socialite_user->ip;
        $this->user_login_code_repository->store($user_id, $code, $expired_at, $ip);

        return $code;
    }

    /**
     * 通过code登陆
     *
     * @param $code
     * @param $ip
     *
     * @return array|void
     */
    public function getUserInfoByLoginCode($code, $ip, $login_user_id = 0)
    {
        if (!$code) {
            return;
        }

        if ($this->isAllowLoginUser($code)) {
            $user_id = $login_user_id > 0 ? $login_user_id : 1;
        } else {
            $user_id = $this->user_login_code_repository->getValidCodeUserId($code);
        }

        if ($user_id <= 0) {
            return;
        }

        $token = $this->getUserToken($user_id, $ip);

        return compact('user_id', 'token');
    }

    /**
     * 获取用户登陆token
     *
     * @param $user_id
     * @param $channel_id
     *
     * @return string
     */
    private function getUserToken($user_id, $ip)
    {
        $token = random_token('sha256');
        $expired_at = Carbon::now()->addMinutes((int)config('auth.login.token_expire'));
        $this->user_token_repository->store($user_id, $token, $expired_at, $ip);

        return $token;
    }

    /**
     * 通过第三方用户来创建用户,获取用户id
     *
     * @param SocialiteUser $socialite_user
     *
     * @return mixed
     */
    public function getUserIdBySocialiteUser(SocialiteUser $socialite_user)
    {
        $socialite_user_model = $this->socialite_user_repository->createOrFind($socialite_user);
        if ($socialite_user_model->user_id <= 0) {
            $user_model = $this->user_repository->store($socialite_user);
            $this->socialite_user_repository->bindUser($socialite_user_model, $user_model->id);

            //绑定邀请人
            $invite_uid = $socialite_user->invite_uid;
            if ($invite_uid) {
                $this->user_repository->storeUserInvite($socialite_user_model->user_id, $invite_uid);
            }
        } else {
            $this->user_repository->update($socialite_user_model->user_id, $socialite_user);
        }

        return $socialite_user_model->user_id;
    }

    /**
     * 用户成为发布者
     *
     * @param $user_id
     * @param $invite_code
     *
     * @throws PublisherException
     * @throws UserException
     */
    public function becomePublisher($user_id, $invite_code)
    {
        if (!$invite_code) {
            throw new PublisherException(PublisherException::PUBLISHER_CODE_NOT_FOUND);
        }
        $user = $this->user_repository->show($user_id);
        if (!$user) {
            throw new UserException(UserException::NOT_FOUND);
        }

        if ($this->publisher_repository->isValidPublisher($user_id)) {
            throw new PublisherException(PublisherException::ALREADY_IS_PUBLISHER);
        }

        $coder = $this->publisher_repository->findCoderByCode($invite_code);
        if (!$coder) {
            throw new PublisherException(PublisherException::PUBLISHER_CODE_NOT_FOUND);
        }
        if ($coder->isUsed()) {
            throw new PublisherException(PublisherException::PUBLISHER_INVITE_CODE_USED);
        }

        //创建发布者
        $this->publisher_repository->createPublisher($user_id);
        //绑定邀请码
        $this->publisher_repository->inviteeBindCoder($coder, $user_id);
    }

    /**
     * 创建邀请码
     *
     * @param $user_id
     *
     * @return mixed
     * @throws PublisherException
     * @throws UserException
     */
    public function createInviteCoder($user_id)
    {
        $user = $this->user_repository->show($user_id);
        if ($user) {
            throw new UserException(UserException::NOT_FOUND);
        }

        if (!$this->publisher_repository->isValidPublisher($user_id)) {
            throw new PublisherException(PublisherException::ONLY_PUBLISHER_CREATE_INVITE_CODE);
        }

        $publisher_invite_code_upper_limit = env('PUBLISHER_INVITE_CODE_UPPER_LIMIT');
        if ($this->publisher_repository->publisherInviteCodeCount() >= $publisher_invite_code_upper_limit) {
            throw new PublisherException(PublisherException::PUBLISHER_INVITE_CODE_UPPER_LIMIT);
        }

        $coder = $this->publisher_repository->createInviteCoder($user_id);

        return $coder->code;
    }

    /**
     * 是否允许登陆指定的用户
     *
     * @param $code
     *
     * @return bool
     */
    private function isAllowLoginUser($code)
    {
        return !app()->environment('production') && $code == env('ALLOW_LOGIN_USER_CODE');
    }
}
